package com.example.JavaWebApi.core.reflect;

import com.example.JavaWebApi.core.reflect.annotation.ChildrenNode;
import com.example.JavaWebApi.core.reflect.annotation.MapperField;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @program: cms_zjgs_api
 * @description: 反射帮助类
 * @author: yang ze
 * @create: 2021-12-02 13:43
 */
public class ReflectionHelper {
    /**
     *
     * 获取所有下级的指定的属性值
     * @param Source 所有的数据源
     * @param getParentCondition 获取第一级父节点的表达式
     * @param targetValueGet 获取指定字段的表达式
     * @param parentValueGet 父节点的主键字段表达式
     * @param childrenValueGet 子节点的pid表达式
     * @param <T> 泛型T
     * @param <TResult> 返回数据的类型
     * @return
     */
    public static <T,TResult> List<TResult> getSubFieldValue(List<T> Source, Predicate<T> getParentCondition,Function<T,TResult> targetValueGet,Function<T,Object> parentValueGet,Function<T,Object> childrenValueGet){
        List<TResult> result=new ArrayList<>();
        for(T item : Source.stream().filter(getParentCondition).collect(Collectors.toList())){
            result.add(targetValueGet.apply(item));
            getChildrenIds(item,Source,targetValueGet,parentValueGet,childrenValueGet,result);
        }
        return result;
    }
    private static <T,TResult> void getChildrenIds(T parentNode,List<T> Source,Function<T,TResult> targetValueGet,Function<T,Object> parentValueGet,Function<T,Object> childrenValueGet,List<TResult> result){
        for(T children :Source.stream().filter(t-> Objects.equals(childrenValueGet.apply(t),parentValueGet.apply(parentNode))).collect(Collectors.toList())){
            result.add(targetValueGet.apply(children));
            getChildrenIds(children,Source,targetValueGet,parentValueGet,childrenValueGet,result);
        }
    }

    /**
     * 通用构造树方法（需要在自包含的字段标记@ChildrenNode）
     * @param Source 数据源（大部分情况是所有的数据）
     * @param getParentCondition 判断是父节点的条件表达式
     * @param parentValueGet 父节点的主键字段表达式
     * @param childrenValueGet 子节点的pid表达式
     * @param Tclass 泛型的类型class
     * @param <T> 泛型类型
     * @return 生成的树型结构
     */
    @SneakyThrows
    public static <T> List<T> convertToTreeList(List<T> Source, Predicate<T> getParentCondition,Function<T,Object> parentValueGet,Function<T,Object> childrenValueGet,Class<T> Tclass){
        Field field=getChildrenNodeField(Tclass);
        List<T> result=new ArrayList<>();
        for(T item : Source.stream().filter(getParentCondition).collect(Collectors.toList())){
            createChildrenNode(item,Source,parentValueGet,childrenValueGet,field);
            result.add(item);
        }
        return result;
    }
    /**
     * Description: 根据注解将一个实体转化为另一个实体(不要用继承)
     * @param data 数据源
	 * @param targetClass 被转换的实体类型
     * @return TResult
     * @author 杨泽
     * @date 2022/4/13 15:50
     */
    @SneakyThrows
    public static <T,TResult> TResult convertToResult(T data, Class <TResult> targetClass){
        HashMap<Field,Field> map=getMapperField(data.getClass(),targetClass);
        TResult result=targetClass.newInstance();
        for(Field key :map.keySet()){
            Field targetField=map.get(key);
            targetField.set(result,key.get(data));
        }
        return result;
    }
    /**
     * Description: 根据注解将一个list转化为目标list（不要用继承）
     * @param dataList 原数据list
	 * @param targetClass 转换类型的class
     * @return java.util.List<TResult>
     * @author 杨泽
     * @date 2022/4/13 15:51
     */
    @SneakyThrows
    public static <T,TResult> List<TResult>convertToResult(List<T> dataList,Class <TResult> targetClass){
        HashMap<Field,Field> map=getMapperField(dataList.get(0).getClass(),targetClass);
        List<TResult> result=new ArrayList<>();
        for (T data:dataList){
            TResult resultData=targetClass.newInstance();
            for(Field key :map.keySet()){
                Field targetField=map.get(key);
                targetField.set(resultData,key.get(data));
            }
            result.add(resultData);
        }
        return result;
    }
    /**
     * Description: 去重
     * @param source 数据源
	 * @param selectors 去重条件
     * @return java.util.List<T>
     * @author 杨泽
     * @date 2022/4/21 9:03
     */
    public static <T> List<T> distinct(List<T> source,Function<T,Object>...selectors){
        HashMap<String,T> hashMap=new HashMap<>();
        List<T> result=new ArrayList<>();
        source.stream().forEach(t->{
            String key="";
            for(Function<T,Object> selector:selectors){
                key+=selector.apply(t);
            }
            if (!hashMap.containsKey(key)){
                hashMap.put(key,t);
            }
        });
        for(String key :hashMap.keySet()){
            result.add(hashMap.get(key));
        }
        return result;
    }
    /**
     * Description: 根据传入的表达式结果作为实体的key,返回一个map用于快速搜索
     * @param source 源数据
	 * @param valueForKey 获取key的表达式
     * @return java.util.HashMap<TKey,T>
     * @author 杨泽
     * @date 2022/4/21 14:32
     */
    @SneakyThrows
    public static <TKey,T> HashMap<TKey,T> getMapForFind(List<T> source, Function<T,TKey> valueForKey){
        HashMap<TKey,T> hashMap=new HashMap<>();
        for(T item:source){
            TKey key=valueForKey.apply(item);
            if(!hashMap.containsKey(key)){
                hashMap.put(key,item);
            }
//            else {
//                throw new Error("作为hashmap的key不可以重复！,请检查传入的条件参数是否为唯一值！key:"+key);
//            }
        }
        return hashMap;
    }
    /**
     * Description: 根据传入的表达式结果作为实体的key,返回一个map用于快速搜索
     * @param source 源数据
     * @param valueForKey 获取key的表达式
     * @param valueForValue 获取map的value的表达式
     * @return java.util.HashMap<TKey,T>
     * @author 杨泽
     * @date 2022/4/21 14:32
     */
    @SneakyThrows
    public static <TKey,T,TResult> HashMap<TKey,TResult> getMapForFind(List<T> source, Function<T,TKey> valueForKey, Function<T,TResult> valueForValue){
        HashMap<TKey,TResult> hashMap=new HashMap<>();
        for(T item:source){
            TKey key=valueForKey.apply(item);
            TResult value=valueForValue.apply(item);
            if(!hashMap.containsKey(key)){
                hashMap.put(key,value);
            }
//            else {
//                throw new Exception("作为hashmap的key不可以重复！,请检查传入的条件参数是否为唯一值！");
//            }
        }
        return hashMap;
    }

    /**
     * Description: 获取映射的字段
     * @param sourceClass 源类型
	 * @param targetClass 目标类型
     * @return java.util.HashMap<java.lang.reflect.Field,java.lang.reflect.Field>
     * @author 杨泽
     * @date 2022/4/21 14:25
     */
    private static HashMap<Field,Field> getMapperField(Class sourceClass,Class targetClass){
        HashMap<Field,Field> map=new HashMap<>();
        List<Field> sourceFields,targetClassFields;
        sourceFields= Arrays.stream(sourceClass.getDeclaredFields()).collect(Collectors.toList());
        targetClassFields=Arrays.stream(targetClass.getDeclaredFields()).collect(Collectors.toList());
        Class sourceSuperClass=sourceClass.getSuperclass();
        Class targetSuperClass=targetClass.getSuperclass();
        while (sourceSuperClass!=Object.class){
            //final字段全都不要
            List<Field> sourceSuperField= Arrays.stream(sourceSuperClass.getDeclaredFields()).filter(t-> !Modifier.isFinal(t.getModifiers())).collect(Collectors.toList());
            sourceFields.addAll(sourceSuperField);
            sourceSuperClass=sourceSuperClass.getSuperclass();
        }
        while (targetSuperClass!=Object.class){
            List<Field> targetSuperField= Arrays.stream(targetSuperClass.getDeclaredFields()).collect(Collectors.toList());
            targetClassFields.addAll(targetSuperField);
            targetSuperClass=targetSuperClass.getSuperclass();
        }
        for (Field sourceField : sourceFields) {
            String targetFieldName ="";
            //标记忽略字段直接跳过
            if (sourceField.isAnnotationPresent(MapperField.class)) {
                MapperField mapperFieldName = sourceField.getAnnotation(MapperField.class);
                if (mapperFieldName.ignore()){
                    continue;
                }
                String[] mapperList=mapperFieldName.mapperName().split(";");
                for(int i=0;i<=mapperList.length-1;i++){
                    //如果标记的映射名带. 那么就把.之前的字段当成类名,.之后的字段当成属性名
                    if(mapperFieldName.mapperName().contains(".")){
                        String[] classAndPropertyName=mapperList[i].split("\\.");
                        //支持用全称和简称 全称有多个.简称必然只有一个
                        String targetClassName="";
                        if (classAndPropertyName.length>2){
                            targetClassName=mapperList[i].substring(0,mapperList[i].lastIndexOf("."));
                            if (targetClassName.equals(targetClass.getName())){
                                targetFieldName=classAndPropertyName[classAndPropertyName.length-1];
                            }
                        }
                        else {
                            targetClassName=classAndPropertyName[0];
                            if (targetClassName.equals(targetClass.getSimpleName())){
                                targetFieldName=classAndPropertyName[classAndPropertyName.length-1];
                            }
                        }

                    }
                    else {
                        targetFieldName = mapperFieldName.mapperName();
                    }
                    String tempTargetFieldName=targetFieldName;
                    if (StringUtils.isNotBlank(tempTargetFieldName)){
                        targetClassFields.stream().filter(t -> t.getName().equals(tempTargetFieldName)).findFirst().ifPresent(t->{
                            if(!map.containsKey(sourceField)){
                                t.setAccessible(true);
                                sourceField.setAccessible(true);
                                map.put(sourceField,t);
                            }
                        });
                    }
                }
            }
            else {
                targetClassFields.stream().filter(t -> t.getName().equals(sourceField.getName())).findFirst().ifPresent(t->{
                    if(!map.containsKey(sourceField)){
                        t.setAccessible(true);
                        sourceField.setAccessible(true);
                        map.put(sourceField,t);
                    }
                });
            }
        }

        return map;
    }


    @SneakyThrows
    private static <T> void createChildrenNode(T parentNode, List<T> Source, Function<T,Object> parentValueGet, Function<T,Object> childrenValueGet, Field field){
        List<T> childrenList=new ArrayList<>();
        for(T children :Source.stream().filter(t-> Objects.equals(childrenValueGet.apply(t),parentValueGet.apply(parentNode))).collect(Collectors.toList())){
            childrenList.add(children);
            createChildrenNode(children,Source,parentValueGet,childrenValueGet,field);
            field.setAccessible(true);
            field.set(parentNode,childrenList);
        }
    }

    @SneakyThrows
    private static Field getChildrenNodeField(Class clazz){
        Optional<Field> op= Arrays.stream(clazz.getDeclaredFields())
                .filter(t->{
                    if (t.isAnnotationPresent(ChildrenNode.class)){
                        if(t.getGenericType() instanceof ParameterizedType){
                            ParameterizedType pt = (ParameterizedType) t.getGenericType();
                            if (pt.getRawType().equals(List.class)) {
                                if (pt.getActualTypeArguments()[0].equals(clazz)){
                                    return true;
                                }
                            }
                        }
                    }
                    return false;
                })
                .findFirst();
        if (op.isPresent()){
            return op.get();
        }
        else {
            throw new Error("请确认实体子节点是否标记ChildrenNode注解且类型为List<"+clazz.getName()+">!");
        }
    }
}